iT邦幫忙

2024 iThome 鐵人賽

DAY 11
0
Software Development

深入淺出Java 30天系列 第 11

Day 11: 覆寫equals時,也要覆寫hashCode(上)

  • 分享至 

  • xImage
  •  

為了避免在使用跟hash有關的功能(ex: HashMapHashSetHashtable)時,因為equals相等但hashCode不一樣,會導致判斷物件存不存在時出現異常,如果有覆寫equals,務必也要覆寫hashCode

覆寫equals和覆寫hashCode,要注意哪些事情?

  • 兩個物件在使用equals判斷相等的情況下,不管觸發幾次hashCode,兩個物件回傳的值必須一致,如果hashCode回傳的值不一樣,有可能會發生很弔詭的情況,明明一樣但一個可以在HashMapHashSetHashtable被找到,另一個卻無法被找到。
    以下面的範例為例,覆寫equals時用identityNumber作為判斷的依據,但是沒有覆寫hashCode,結果p1p2雖然用equals判斷是一樣,但hash code不一樣,要從HashMap取出p2值的時候,卻找不到p2
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

class Person {
    private String identityNumber;
    private int age;

    public Person(String identityNumber, int age) {
        this.identityNumber = identityNumber;
        this.age = age;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return Objects.equals(identityNumber, person.identityNumber);
    }

    public static void main(String[] args) {
        Person p1 = new Person("R12345", 30);
        Person p2 = new Person("R12345", 30);

        Map<Person, String> map = new HashMap<>();
        map.put(p1, "Person 1");

        System.out.println(p1.equals(p2)); // true
        System.out.println("p1 hash code:" + p1.hashCode() + ", map get result: "+ map.get(p1)); // p1 hash code:705927765, map get result: Person 1
        System.out.println("p2 hash code:" + p2.hashCode() + ", map get result: "+ map.get(p2)); // p2 hash code:366712642, map get result: null
    }
}
  • 如果用equals判斷兩個物件不相等,hashCode回傳的值最好也不相等。hashCode不相等除了可以減少hash collision,也可以提升搜尋key的效能。
  • 如果物件是immutable且hash code又很重要,可以考慮cache hashCode的結果,不用每次都重新計算一次,能改善一些效能問題。cache的方法可以選擇使用lazily initialized去cache住hash code,宣告一個volatile的變數儲存hash code。
class PersonForHash {
    private boolean isAdult; //0=male, 1=female
    private String name;
    private int age;

    private volatile int hashCode;

    public PersonForHash(boolean isAdult, String name, int age) { 
        this.isAdult = isAdult;
        this.name = name;
        this.age = age;
    }

    @Override
    public int hashCode() {
        int result = hashCode;
        if (result == 0) {
            result = 17;
            result = 31 * result + age;
            result = 31 * result + (isAdult ? 1 : 0); 
            result = 31 * result + (name != null ? name.hashCode() : 0);
            hashCode = result;
        }
        return result;
    }
}

明天再繼續介紹,可以遵照哪些規則覆寫hashCode


上一篇
Day 10: 覆寫equals時的注意事項和規範(下)
下一篇
Day 12: 覆寫equals時,也要覆寫hashCode(下)
系列文
深入淺出Java 30天30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言